#include "stdafx.h"
#include "database.h"
#include "Server.h"
#include "minilzo.h"






//data formate    cmd + data.
//cmd :   1 file. 2.file complete, 3. file complete confirm. 4.delete file

//data format     filename + filedata

//filename format  len + name

Server::Server(void)
{
	m_mode = 0; // 0 正常文件发送模式
	connectDB();
	recFlieList=CreateUdxList();
}


Server::~Server(void)
{
	closeDB();
	recFlieList->Destroy();
}

void Server::OnStreamConnect(IUdxTcp * pTcp,int erro)
{
	//2022-06-01 hzh 小文件 也要设置路径
	//if(m_mode == 0) // 正常文件模式 大文件  
	pTcp->SetFSink(this);

	//pTcp->GetUdxCfg()->hasbp = 1;	
	//pTcp->SetSaveFileDir((char*)m_savedir.c_str());//路径设置在接收文件时做
	//SOCKADDR_IN *tmpsock;
	//tmpsock = pTcp->GetRemoteAddr();
	sockaddr_in *tmpsock = ( struct sockaddr_in*)pTcp->GetRemoteAddr();
	int port = ntohs(tmpsock->sin_port);
	in_addr in  = tmpsock->sin_addr;

	if ( RELEASEFOR == "EN")
	{
		getMyTool()->printStr("recive connect from %s:%d,ID: %d",inet_ntoa(in),port,pTcp->GetStreamID());	
	}else if(RELEASEFOR == "CH"){
		getMyTool()->printStr("接受 %s:%d 链接请求,ID: %d",inet_ntoa(in),port,pTcp->GetStreamID());	
	}

	
	//20231115 这里声明局部变量 开辟内存区域 用tcp链接中的特性，将指针存下来
	tcpTransFileState* m_tcpTransFileState = new tcpTransFileState;
	pTcp->SetUserData((UINT64)m_tcpTransFileState);
}
void Server::OnStreamBroken(IUdxTcp * pTcp)
{
	//20231115 全过程中 一直通过tcp链接中存储的地址 来指明结构体地址
	tcpTransFileState* m_tcpTransFileState =(tcpTransFileState*) pTcp->GetUserData();
	//这个tcp链接消亡时 删除开辟的存储区域即可 其他地方可以重新建立局部变量 指明 但不能删除
	delete m_tcpTransFileState;
	pTcp->SetUserData(0);
	
}
//void Server::OnStreamLinkIdle(IUdxTcp *pTcp)
//{
	//GetUdxTools()->DBGStr("streamlinkidle 触发的速度显示--接收\n");
	//getMyTool()->printStr("streamlinkidle 触发的速度显示--接收");	
	//UpdateSpeed(pTcp);

//}
void Server::FileTransmitDone(IUdxTcp*pTcp,char* filename,BOOL bSend)
{
	//大文件才会进入这个过程 在这里边需要些fifo
	getMyTool()->printStr("recive file [%s] ok",filename);
	UpdateSpeed2(pTcp);

	IUdxInfo* pInfo = pTcp->GetUdxInfo();
	reciveFileRecord tmpRc;
	char m_NowTime[32];
	//memset(&tmpRc,0,sizeof(tmpRc));
	getNowTime(m_NowTime);
	strcpy(tmpRc.reciveTime,m_NowTime);
	strcpy(tmpRc.fileName,filename);
	tmpRc.speed = pInfo->GetSpeed(FALSE);
	tmpRc.fsizeB=pTcp->GetTotalReadfileLen();
	tmpRc.ms=0;
	int bytelength=sizeof(tmpRc);
	BYTE *p=new BYTE[bytelength]; 
	memmove(p,&tmpRc,bytelength);
	recFlieList->AddBuff(p,bytelength);
	delete []p;
	
}
//void Server::OnStreamFinalRelease(IUdxTcp * pTcp)
//{
	//UpdateSpeed2(pTcp);
//}

//void Server::OnFileStreamConnect(IUdxTcp * pTcp,int erro)
//{
	//printf("FHL %s >>new normal file mode reciving\n",getNowTime());
	
//}
/*
hzh
服务端在收到流后的处理
1小文件模式中收到文件并落盘 返回客户端4类型 以及文件名 用于客户端删除文件 发送 最多发送10次
2 文件发完后会触发这个过程
3知道作用
4知道作用
5客户端在大文件模式中 发过来的相对路径 本地要判断是否存在对应路径 没有就创建 返回5type 以及是否可以传输
8 发送端传输的文件包 2M一个
  default 意外命令
*/
void Server::OnStreamRead(IUdxTcp * pTcp,BYTE* pData,int len)
{
	//m 0  正常发文件模式
	//经过测试 大文件并不会触发这个函数 不会进入streamread
	//20220606 hzh 修改 大文件也要触发这个read
//	if(m_mode == 0){
//		printf("FHL %s >>大文件模式收到stream %s \n",getNowTime(),(char*)pData);
//		return;
//	}
	//GetUdxTools()->DBGStr("收到streamRead流 \n");	
	BYTE cmd = *pData;
	GetUdxTools()->DBGStr("收到OnStreamRead %d\n",cmd);
	//getMyTool()->printStrandDB("recive OnStreamRead cmd %d",cmd);
	//char NowTime[32];
	//整个包的第一个字节是类型区分用的
	switch(cmd)
	{
	case 1://smallfile	
			recvSmallFile(pTcp,pData,len);
			break;
		//20230316 这个回执并没有实际意义 不能在这里保证文件完整
	case 2://file complete 大小文件模式都会触发 列表中的都发完了 
		{
			GetUdxTools()->DBGStr("recv complete msg!\n");
			UpdateSpeed2(pTcp);//传完后刷新显示速度
			BYTE data[1] = {0};
			data[0] = 3;
			int trycount = 20;
			while(trycount -- )
			{
				if(pTcp->SendBuff(data,1))
					break;
				else
				{
					GetUdxTools()->DBGStr("send complete msg faild\n");
					GetUdxTools()->UdxSleep(10);
				}
			}
		}
		break;
	case 3://file complete confirm. 
		{
		}
		break;
	case 4://delete file
		{
		}
		break;
	case 5://大文件 接收相对路径和文件名
		recvBigFileHeader(pTcp,pData,len);
		break;
	case 8://收到大文件包
		recvBigFilePackage(pTcp,pData,len);
		break;	
	case 9://收到心跳包
		{
			sockaddr_in *tmpsock = ( struct sockaddr_in*)pTcp->GetRemoteAddr();
			int port = ntohs(tmpsock->sin_port);
			in_addr in  = tmpsock->sin_addr;
			if ( RELEASEFOR == "EN")
			{
				getMyTool()->printStr("recive heart packet %s:%d ID: %d",inet_ntoa(in),port,pTcp->GetStreamID());		
			}else if(RELEASEFOR == "CH"){
				getMyTool()->printStr("收到客户端 %s:%d 心跳,ID: %d",inet_ntoa(in),port,pTcp->GetStreamID());	
			}

			
		}
		break;	
	default:
		{
			GetUdxTools()->DBGStr("unknown cmd %u - %d\n",cmd,len);
		}
		break;
	}

}
void Server::recvSmallFile(IUdxTcp * pTcp,BYTE* pData,int len)
{
	IUdxInfo* pInfo = pTcp->GetUdxInfo();
	reciveFileRecord tmpRc;
	
	BYTE filenamelen = *(pData + 1);
	long filedatalen;
	memcpy(&filedatalen,pData + 2,sizeof(long));
	//首先获取速度
	int compx = filedatalen/(len - filenamelen - 2-sizeof(long));
	compx==0?compx=1:compx=compx;
	tmpRc.speed = pInfo->GetSpeed(FALSE)*compx;
	char szFile[256] = {0};
	memset(szFile,'\0',256);
	int starttime = MyGetTick();
	//1(类型标志位)+1(文件名长度)+sizeof(long)发送原始包长度+文件名+压缩后数据包
	memcpy(szFile,pData + 2+sizeof(long),filenamelen);//将文件名读出来

	if ( RELEASEFOR == "EN")
	{
		getMyTool()->printStr("recive file  [%s] length [%d Bytes]\n",szFile,len);	
	}else if(RELEASEFOR == "CH"){
		getMyTool()->printStr("收到文件 [%s] 总长度 [%d Bytes]\n",szFile,len);
	}

	

	string rootpath = getSavePath(m_savedir,szFile);
	if(rootpath=="")
	{
		getMyTool()->printStr("cannot create dir %s",szFile);
		return;
	};
	string strFileName = m_savedir + "/" + szFile;		
	//c++的文件创建
	FILE *pFileOut=fopen((char*)strFileName.c_str(),"wb");
	if(!pFileOut)
	{
		
		GetUdxTools()->UdxSleep(10);
		GetUdxTools()->DBGStr("CreateFile new file %s faild\n",szFile);
		getMyTool()->printStrandDB("can not creat file %s ",szFile);
		return;
	
	}
	/************************************************************************/
	/* 解压缩                                                               */
	/************************************************************************/
	
	if (m_bLZO)
	{
		lzo_uint in_len = filedatalen;//发送端告知的原始长度
		lzo_uint out_len = len - filenamelen - 2-sizeof(long);
		lzo_uint new_len;
		lzo_bytep in;
		lzo_bytep out;
		lzo_voidp wrkmem;
		wrkmem = (lzo_voidp) malloc(LZO1X_1_MEM_COMPRESS);
		in = (lzo_bytep) malloc(in_len );
		out = (lzo_bytep) malloc(in_len +in_len /16+64+3);
		memcpy(out,pData + 2+sizeof(long)+filenamelen,len - filenamelen - 2-sizeof(long));

		int	r = lzo1x_decompress(out,out_len,in,&new_len,NULL);
		if (r == LZO_E_OK && new_len == in_len)
			getMyTool()->printStr("解压 %lu bytes back into %lu bytes",
				(unsigned long) out_len, (unsigned long) in_len);
		else
		{
			/* this should NEVER happen */
			getMyTool()->printStr("解压失败，请确认压缩模式: %d\n", r);
			
		}

		
		fwrite(in,in_len,1,pFileOut);
		
		free(wrkmem);
		free(in);
		free(out);
	}else{
		if (filedatalen == (len - filenamelen - 2-sizeof(long)))
		{
			fwrite(pData + 2+sizeof(long)+filenamelen,filedatalen,1,pFileOut);
		}else{
			getMyTool()->printStr("收到压缩数据，请确认压缩模式");
		}	
		
	}
	fclose(pFileOut);

	//数据库操作
	INT64 fsizeB = filedatalen;
	//文件大小
	
	
	//将文件放到fifo队列里边

	char m_NowTime[32];
	//memset(&tmpRc,0,sizeof(tmpRc));
	getNowTime(m_NowTime);
	strcpy(tmpRc.reciveTime,m_NowTime);
	strcpy(tmpRc.fileName,szFile);
	
	tmpRc.fsizeB=fsizeB;
	tmpRc.ms=MyGetTick()-starttime;
	int bytelength=sizeof(tmpRc);
	BYTE *p=new BYTE[bytelength]; 
	memmove(p,&tmpRc,bytelength);
	recFlieList->AddBuff(p,bytelength);
	delete []p;

	if ( RELEASEFOR == "EN")
	{
		getMyTool()->printStr("recived file: %s file size: [%I64d Bytes],speed [ %s ]",szFile,fsizeB,ToString(tmpRc.speed).c_str());
	}else if(RELEASEFOR == "CH"){
		getMyTool()->printStr("接收文件: %s 文件长度: [%I64d Bytes],速度 [ %s ]",szFile,fsizeB,ToString(tmpRc.speed).c_str());
	}

	
}
void Server::recvBigFileHeader(IUdxTcp * pTcp,BYTE* pData,int len)
{

	/************************************************************************/
	/* 创建相对路径，有的话就不创建                                         */
	/************************************************************************/
	char szFile[256];//相对路径
	memset(szFile,0,sizeof(szFile));
	memcpy(szFile,pData + 1+sizeof(UINT64),len-1-sizeof(UINT64));
	string rootpath = getSavePath(m_savedir,szFile);//文件保存路径 最后没有斜杠
	if(rootpath!="")
	{
		pTcp->SetSaveFileDir((char*)rootpath.c_str());
	}else{
		getMyTool()->printStr("cannot create dir ,while use :%s",m_savedir.c_str());
		pTcp->SetSaveFileDir((char*)m_savedir.c_str());//如果创建文件路径失败则存放在根目录下
	};
	/************************************************************************/
	/*    构建完整文件名 以及临时文件名                                     */
	/************************************************************************/
	char tmpFile[256];//临时文件 文件名
	char fullFile[256];//真实完整文件名
	memset(tmpFile,0,sizeof(tmpFile));
	memset(fullFile,0,sizeof(fullFile));
	strcpy(tmpFile,m_savedir.c_str());
	strcat(tmpFile,"/");
	strcat(tmpFile,szFile);
	strcpy(fullFile,tmpFile);//完整文件名
	strcat(tmpFile,GetUdxGlobalCfg()->ext);//.TFHL
	/*****************文件结构体实例化*****************************************/
	tcpTransFileState* m_tcpTransFileState =(tcpTransFileState*) pTcp->GetUserData();
	//udx存储的正在传输的文件信息结构体 是否损坏
	if (m_tcpTransFileState==0)
	{
		BYTE senddata = 7;
		while (!pTcp->SendBuff(&senddata,1))
		{
			GetUdxTools()->UdxSleep(100);
		} 
		//结束传输 删除实例
		//delete m_tcpTransFileState;
		getMyTool()->printStr("链接中的文件结构损坏");
		return;
	}
	
	//文件长度和文件名赋值
	memcpy(&m_tcpTransFileState->fileLength,pData + 1,sizeof(UINT64));
	//memcpy(&m_tcpTransFileState->fileLength ,pData + 1,sizeof(UINT64));
	memcpy(m_tcpTransFileState->szFile,fullFile,sizeof(fullFile));
	memcpy(m_tcpTransFileState->szFileTmp,tmpFile,sizeof(tmpFile));

	if ( RELEASEFOR == "EN")
	{
		getMyTool()->printStr("big file type recive header:%s %I64u Bytes",m_tcpTransFileState->szFile,m_tcpTransFileState->fileLength);
	}else if(RELEASEFOR == "CH"){
		getMyTool()->printStr("大文件模式收到头文件:%s %I64u Bytes",m_tcpTransFileState->szFile,m_tcpTransFileState->fileLength);
	}

	
	/************************************************************************/
	/*    判断并          创建本地文件                                      */
	/************************************************************************/
	//if (m_tcpTransFileState->pFile->IsExistFile(fullFile))
	if (access(fullFile, F_OK)==0)	
	{
		//这里有个遗留隐患 没有判断文件是否完整 校验可能会浪费时间
		//所以只通过文件名校验了
		if ( RELEASEFOR == "EN")
		{
			getMyTool()->printStr("there is same file ,reply success");
		}else if(RELEASEFOR == "CH"){
			getMyTool()->printStr("存在同名文件,返回传输成功标志");
		}

		
		//发送成功标志6 
		BYTE senddata = 6;
		
		while (!pTcp->SendBuff(&senddata,1))
		{
			GetUdxTools()->UdxSleep(100);
		} 
		//结束传输 删除实例
		//delete m_tcpTransFileState;
		return;
	}
//不存在同名 判断临时文件
	getMyTool()->printStr("temp file ?%s",tmpFile);
	if (m_tcpTransFileState->pFile->IsExistFile(tmpFile))
	{
		
		if(m_tcpTransFileState->pFile->IsOpenByOther(tmpFile))
		{
			//getMyTool()->printStr("存在临时文件,但被其他进程打开了");
			//接收失败
			BYTE senddata = 7;
			while (!pTcp->SendBuff(&senddata,1))
			{
				GetUdxTools()->UdxSleep(100);
			} 
		//结束传输 删除实例
			//delete m_tcpTransFileState;
			return;
		}
	

		if (m_tcpTransFileState->pFile->OpenFile(tmpFile))
		{
			/************如果临时文件长度和实际文件长度相等 则直接成功**********/
			UINT64 fl = m_tcpTransFileState->pFile->GetFileLength();
			m_tcpTransFileState->pFile->Close();
			//这里不能删除实例 为什么
			if (fl == m_tcpTransFileState->fileLength)
			{
				getMyTool()->printStr("存在已传输完成的临时文件");
				BYTE senddata = 6;
				while (!pTcp->SendBuff(&senddata,1))
				{
					GetUdxTools()->UdxSleep(100);
				} 
				//文件重命名
				rename(m_tcpTransFileState->szFileTmp,m_tcpTransFileState->szFile);
				//结束传输 删除实例
				
				//delete m_tcpTransFileState;
				//getMyTool()->printStr("传完了，删除结构体");
				return;
			}else if (fl < m_tcpTransFileState->fileLength)
			{
				if ( RELEASEFOR == "EN")
				{
					getMyTool()->printStr("find tmp file,wihle trans continue");
				}else if(RELEASEFOR == "CH"){
					getMyTool()->printStr("存在临时文件,开始断点续传");
				}

				
				//返回8可以传输
				BYTE senddata[1+sizeof(UINT64)];
				senddata[0]=8;
			
				memcpy(&senddata[1],&fl,sizeof(fl));
				
				while (!pTcp->SendBuff(senddata,1+sizeof(UINT64)))
				{
					GetUdxTools()->UdxSleep(100);
				} 
				m_tcpTransFileState->pMyFile = fopen(tmpFile,"ab+");
				m_tcpTransFileState->transedLength = fl;
				return;
			}else{
				getMyTool()->printStr("文件大小异常，删除后接收");
				remove(tmpFile);
				BYTE senddata = 7;
				
				while (!pTcp->SendBuff(&senddata,1))
				{
					GetUdxTools()->UdxSleep(100);
				} 
				//结束传输 删除实例
				//delete m_tcpTransFileState;
				return;
			}
				
			
		}else{
			/******临时文件打开失败**************/
			//返回接收失败。。补全代码
			BYTE senddata = 7;
			while (!pTcp->SendBuff(&senddata,1))
			{
				GetUdxTools()->UdxSleep(100);
			} 
			//结束传输 删除实例
			//delete m_tcpTransFileState;
			return;
		}
	}else
	{
/*************************不存在临时文件就创建*******************************/
		
		if (m_tcpTransFileState->pFile->CreateFile(tmpFile))
		{
			getMyTool()->printStr("create temp file %s",tmpFile);
			//m_tcpTransFileState->pFile->OpenFile(tmpFile);
			BYTE senddata[1+sizeof(UINT64)];
			senddata[0]=8;
			UINT64 fl = 0;
			m_tcpTransFileState->transedLength=0;
			memcpy(&senddata[1],&fl,sizeof(fl));
			
			while (!pTcp->SendBuff(senddata,1+sizeof(UINT64)))
			{
				GetUdxTools()->UdxSleep(100);
			} 

			m_tcpTransFileState->pFile->Close();
			m_tcpTransFileState->pMyFile = fopen(tmpFile,"ab+");
			
			return;
			
		}else{
			getMyTool()->printStr("can not create temp file%s",tmpFile);
			BYTE senddata = 7;
			while (!pTcp->SendBuff(&senddata,1))
			{
				GetUdxTools()->UdxSleep(100);
			} 
		//结束传输 删除实例
			//delete m_tcpTransFileState;
			return;
		}
	}
	
	
	
}
void Server::recvBigFilePackage(IUdxTcp * pTcp,BYTE* pData,int len)
{
	tcpTransFileState * m_pTcpTransFileState = (tcpTransFileState*) pTcp->GetUserData();
	//	GetUdxTools()->DBGStr("收到数据包");
	if (m_pTcpTransFileState==0)
	{
		getMyTool()->printStr("链接结构体异常,拒绝接收");
			return;
	}
	
	
	int writelong=-1;
	lzo_uint in_len;
	memcpy(&in_len,pData + 1,sizeof(long));
	 
	try
	{
		if (m_bLZO)
		{
			//1(类型标志位)++sizeof(long)原始包长度+压缩后数据包(最大长度in_len +in_len /16+64+3)
			
			
			lzo_uint out_len = len - 1-sizeof(long);
			lzo_uint new_len = 0;
			int	r = lzo1x_decompress(pData+1+sizeof(long),out_len,m_pTcpTransFileState->in,&new_len,NULL);
			
			if (in_len == new_len)
			{
				//getMyTool()->printStr("解压 %lu bytes  into %lu bytes [%.2f]倍",
				//(unsigned long) out_len, (unsigned long) new_len,(double)new_len/out_len);
				writelong = fwrite(m_pTcpTransFileState->in,new_len,1,m_pTcpTransFileState->pMyFile) ;
			}else
			{
				getMyTool()->printStr("解压缩失败，请检查[压缩]模式");	
			}
			

		}else{
			if(in_len == (len - 1-sizeof(long))){
				writelong = fwrite(pData+1+sizeof(long),in_len,1,m_pTcpTransFileState->pMyFile) ;
			}else{
				getMyTool()->printStr("非压缩模式收到压缩数据，请确认[压缩]模式[%lu-%lu]",in_len , (len - 1-sizeof(long)));
			}
			
		}
		
		//sendlong = m_pTcpTransFileState->pFile->Write(pData+1,(len-1));
	}
	catch(...) 
	{
		getMyTool()->printStr("文件落盘故障，并检查[压缩]模式...");

	}
	
	if (writelong==1)
	{
	//写文件成功后
		m_pTcpTransFileState->transedLength+=in_len;
		//GetUdxTools()->DBGStr("文件长度%I64u---%I64u",m_pTcpTransFileState->fileLength,m_pTcpTransFileState->transedLength);
		UpdateFileTrans(m_pTcpTransFileState->fileLength,m_pTcpTransFileState->transedLength);
		//GetUdxTools()->DBGStr("写入长度-%d Bytes 进度[%d%%]",in_len,	m_pTcpTransFileState->transedLength*100/m_pTcpTransFileState->fileLength);
		//getMyTool()->printStr("落盘后transedLength位置%I64u",m_pTcpTransFileState->transedLength);
		if(m_pTcpTransFileState->fileLength<=m_pTcpTransFileState->transedLength)
		{
			
			
			int trycount=10;
			while (trycount--)
			{
				if(fclose(m_pTcpTransFileState->pMyFile)==0)
				{
					break;
				}
				getMyTool()->printStr("关闭文件失败,尝试%d次",trycount);
				GetUdxTools()->UdxSleep(100);

			}
			trycount=10;
			while (trycount--)
			{
				if(rename(m_pTcpTransFileState->szFileTmp,m_pTcpTransFileState->szFile)==0)
				{
					break;
				}
				getMyTool()->printStr("修改临时文件[%s]名为[%s]失败,尝试%d次,文件errno-%d",m_pTcpTransFileState->szFileTmp,
					m_pTcpTransFileState->szFile,trycount,
					errno);
				GetUdxTools()->UdxSleep(100);

			}
			//结束传输 删除实例
			//delete m_pTcpTransFileState;	
			//20231115 改完文件名后再返回确认信息
			//文件已经接受完毕
			BYTE senddata = 6;
			//BlockSendBuff(1,pSendData,filenamelen + 1+sizeof(UINT64),3000)
			pTcp->BlockSendBuff(1,&senddata,1,3000);

			if ( RELEASEFOR == "EN")
			{
				getMyTool()->printStr("file have been recived,reply ok");	
			}else if(RELEASEFOR == "CH"){
				getMyTool()->printStr("接收完毕，已发送6号回执");	
			}

			
		}
	}
	else
	{
		BYTE senddata = 7;
		
		while (!pTcp->SendBuff(&senddata,1))
		{
			GetUdxTools()->UdxSleep(100);
		} 
		fclose(m_pTcpTransFileState->pMyFile);
	//结束传输 删除实例
		//delete m_pTcpTransFileState;
		//pTcp->SetUserData(0);
		getMyTool()->printStr("[落盘]异常，发送接收失败回执");
	}
	
	
	//GetUdxTools()->UdxSleep(1000);
}
void Server::OnStreamMsgRead(IUdxTcp * pTcp,BYTE* pData,int len)
{

	
	
}
